<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>混入</title>
    <script src="../vue.js"></script>
  </head>
  <body>
    <!-- 1 基础 -->
<!--    <div id="app-1" style="border: 1px dashed red;margin-bottom: 2px;">
      <component-a></component-a>
    </div> -->
    <!-- 2 选项合并 -->
    <!-- 3 全局混入 -->
    <!-- 4 自定义选项合并策略 -->
    <script type="text/javascript">
      // 1 基础
      //   定义一个混入对象
      var myMixin = {
        created: function() {
          this.hello()
        },
        methods: {
          hello: function() {
            console.log('hello from mixin!')
          }
        }
      }
      
      //   定义一个使用混入对象的组件
      var Component = Vue.extend({
        mixins: [myMixin]
      })
      
      var component = new Component() // => hello from mixin!
      
      // 2 选项合并
      //   当组件和混入对象含有同名选项时，这些选项将以恰当的方式进行“合并”。
      //   比如，数据对象在内部会进行递归合并，并在发生冲突时以组件数据优先。
      var mixin = {
        data: function() {
          return {
            message: 'hello',
            foo: 'abc'
          }
        }
      }
      
      new Vue({
        mixins: [mixin],
        data: function() {
          return {
            message: 'goodbye',
            bar: 'def'
          }
        },
        created: function() {
          console.log(this.$data)
          // => { message: "goodbye", foo: "abc", bar: "def" }
        }
      })
      //   同名钩子函数将合并为一个数组，因此都将被调用。另外，混入对象的钩子将在组件自身钩子之前调用。
      var mixin2 = {
        created: function() {
          console.log('混入对象的钩子被调用')
        }
      }
      
      new Vue({
        mixins: [mixin2],
        created: function() {
          console.log('组件自身的钩子被调用')
        }
      })

      //   值为对象的选项，例如 methods、components 和 directives，将被合并为同一个对象。两个对象键名冲突时，取组件对象的键值对。
      var mixin3 = {
        methods: {
          foo: function() {
            console.log('foo')
          },
          conflicting: function() {
            console.log('from mixin')
          }
        }
      }
      
      var vm = new Vue({
        mixins: [mixin3],
        methods: {
          bar: function() {
            console.log('bar')
          },
          conflicting: function() {
            console.log('from vue instance')
          }
        }
      })
      //   注意：Vue.extend() 也使用同样的策略进行合并。

      // 3 全局混入
      //   混入也可以进行全局注册。使用时格外小心！一旦使用全局混入，它将影响每一个之后创建的 Vue 实例。使用恰当时，这可以用来为自定义选项注入处理逻辑。

      //   为自定义的选项 'myOption' 注入一个处理器。
      Vue.mixin({
        created: function() {
          var myOption = this.$options.myOption
          if (myOption) {
            console.log(myOption)
          }
        }
      })
      
      new Vue({
        myOption: 'hello!'
      })
      // => hello!
      
      // 4 自定义选项合并策略
      //   自定义选项将使用默认策略，即简单地覆盖已有值。如果想让自定义选项以自定义逻辑合并，可以向 Vue.config.optionMergeStrategies 添加一个函数：
      Vue.config.optionMergeStrategies.myOption = function(toVal, fromVal) {
        // 返回合并后的值
      }
      
      // 对于多数值为对象的选项，可以使用与 methods 相同的合并策略：
      var strategies = Vue.config.optionMergeStrategies
      strategies.myOption = strategies.methods
      
      // 可以在 Vuex 1.x 的混入策略里找到一个更高级的例子：
      const merge = Vue.config.optionMergeStrategies.computed
      Vue.config.optionMergeStrategies.vuex = function(toVal, fromVal) {
        if (!toVal) return fromVal
        if (!fromVal) return toVal
        return {
          getters: merge(toVal.getters, fromVal.getters)
          state: merge(toVal.state, fromVal.state)
          actions: merge(toVal.actions, fromVal.actions)
        }
      }
    </script>
  </body>
</html>
